package com.cloud.rou.im.client.api.event;

import com.cloud.rou.im.client.msg.MsgRequestPool;
import com.cloud.rou.im.client.msg.MsgResponsePool;
import com.cloud.rou.im.client.context.UserContextHolder;
import com.cloud.rou.im.client.msg.ChatMsgQueueExecutor;
import com.cloud.rou.im.core.base.ResultVO;
import com.cloud.rou.im.core.exception.IMException;
import com.cloud.rou.im.msg.AckMsg;
import com.cloud.rou.im.msg.ChatMsg;
import com.cloud.rou.im.msg.Msg;
import com.cloud.rou.im.msg.MsgFactory;
import com.cloud.rou.im.msg.enumtype.AckType;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 */
@Slf4j
public class ClientEventService {

    private MsgResponsePool responsePool = MsgResponsePool.getInstance();
    private MsgRequestPool requestPool = MsgRequestPool.getInstance();
    private ChatMsgQueueExecutor executor = ChatMsgQueueExecutor.instance();

    /**
     * the client online
     */
    @BindClientEvent(event = IMClientEvent.online)
    public void online(AckMsg msg) {
        log.info("login success [userId : {} ]",msg.getRecipientId());
        responsePool.add(msg.getAckMsgId(),ResultVO.success(msg.getAckMsgId()));
    }

    /**
     * the client offline
     */
    @BindClientEvent(event = IMClientEvent.offline)
    public void offline() {
        System.out.println("off");
    }
    /**
     * the client offline
     */
    @BindClientEvent(event = IMClientEvent.read)
    public void read(ChatMsg msg) {
        log.info("receive one msg:{}[msgId:{}]",msg.getMsgBody(),msg.getMsgId());
        // chatMsg to AckMsg
        Msg ackMsg = MsgFactory.chatToAck(() -> AckType.READ, msg);
        UserContextHolder.writeAndFlush(ackMsg);
    }

    /**
     * the client has send one msg
     */
    @BindClientEvent(event = IMClientEvent.hasSend)
    public ResultVO hasSend(Msg msg) {
      if(msg instanceof ChatMsg){
          // push -> queue
          requestPool.add(msg);
      }return null;
    }

    /**
     * the client has read one msg
     */
    @BindClientEvent(event = IMClientEvent.hasRead)
    public void hasRead(AckMsg msg) {
      log.info("the msg has read [msgId:{}]",msg.getAckMsgId());
        Msg msg1 = requestPool.get(msg.getAckMsgId());
        if(msg1 instanceof ChatMsg){
            ChatMsg chatMsg = (ChatMsg) msg1;
            executor.remove(chatMsg);
        }
    }

    /**
     * the client has delivered one msg
     */
    @BindClientEvent(event = IMClientEvent.hasDelivered)
    public void hasDelivered(AckMsg ackMsg) {
        Long id = ackMsg.getAckMsgId();
        responsePool.add(id,ResultVO.success(id));
        log.info("the msg has delivered [msgId:{}]",id);
        // 处理回执
        Msg msg = requestPool.get(id);
        if(msg instanceof ChatMsg){
            ChatMsg chatMsg = (ChatMsg) msg;
            executor.add(chatMsg);
        }
    }

    /**
     * exception
     */
    @BindClientEvent(event = IMClientEvent.exception)
    public void exception(Throwable cause) {
        throw new IMException("An exception occurs,the reason:"+cause.getMessage());
    }
    public void methodInvokeByEvent(IMClientEvent event,Object... objects)  {
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            BindClientEvent annotation = method.getAnnotation(BindClientEvent.class);
            if(annotation!=null&&annotation.event()==event){
                if(objects.length == 0 ){
                    try {
                        method.invoke(this,null);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new IMException( String.format("the method [%s] occur exception",method.getName()));
                    }
                }else {
                    try {
                        method.invoke(this,objects[0]);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new IMException( String.format("the method [%s] occur exception",method.getName()));
                    }
                }
                break;
            }
        }
    }
}
